package com.room.search.service;

import com.room.search.constant.Constants;
import com.room.search.constant.HttpStatus;
import com.room.search.core.page.TableDataInfo;
import com.room.search.core.redis.RedisCache;
import com.room.search.domain.Authority;
import com.room.search.domain.RoomUser;
import com.room.search.domain.RoomUserPermission;
import com.room.search.repository.AuthorityRepository;
import com.room.search.repository.RoomUserPermissionRepository;
import com.room.search.repository.RoomUserRepository;
import com.room.search.response.RoomResult;
import com.room.search.security.AuthoritiesConstants;
import com.room.search.security.SecurityUtils;
import com.room.search.security.jwt.TokenProvider;
import com.room.search.service.dto.GetBrokerDTO;
import com.room.search.service.dto.RoomUserDTO;
import com.room.search.service.dto.broker.BrokerRegisterDTO;
import com.room.search.service.dto.user.ChangeActivatedDTO;
import com.room.search.service.dto.user.RoomUserAddDTO;
import com.room.search.service.dto.user.UserRegisterDTO;
import com.room.search.service.mapper.RoomUserMapper;
import com.room.search.service.sms.ISmsService;
import com.room.search.util.BeanValidator;
import com.room.search.util.PageUtils;
import com.room.search.util.RegexCheckUtil;
import com.room.search.web.rest.vm.UserMobileLoginVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.Predicate;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Service Implementation for managing {@link RoomUser}.
 */
@Service
@Transactional
public class RoomUserService {

    private final Logger log = LoggerFactory.getLogger(RoomUserService.class);

    private static final String BROKER = "ROLE_BROKER";

    private static final String DEFAULT_AVATAR = "https://gitee.com/guo-dashuaibright/photo/raw/master/2022110.jpg";

    private final RoomUserRepository roomUserRepository;

    private final RoomUserMapper roomUserMapper;

    private final AuthenticationManagerBuilder authenticationManagerBuilder;
    private final ISmsService iSmsService;
    private final BCryptPasswordEncoder encoder;
    private final TokenProvider tokenProvider;
    private final AuthorityRepository authorityRepository;
    private final RoomUserPermissionRepository roomUserPermissionRepository;
    private final RedisCache redisCache;

    public RoomUserService(RoomUserRepository roomUserRepository,
                           RoomUserMapper roomUserMapper,
                           AuthenticationManagerBuilder authenticationManagerBuilder,
                           ISmsService iSmsService, BCryptPasswordEncoder encoder,
                           TokenProvider tokenProvider, AuthorityRepository authorityRepository,
                           RoomUserPermissionRepository roomUserPermissionRepository,
                           RedisCache redisCache
    ) {
        this.roomUserRepository = roomUserRepository;
        this.roomUserMapper = roomUserMapper;
        this.authenticationManagerBuilder = authenticationManagerBuilder;
        this.iSmsService = iSmsService;
        this.encoder = encoder;
        this.tokenProvider = tokenProvider;
        this.authorityRepository = authorityRepository;
        this.roomUserPermissionRepository = roomUserPermissionRepository;
        this.redisCache = redisCache;
    }

    /**
     * 发送验证码
     * @param phone 手机号码
     */
    public String sendSms(String phone) {
        if (phone.length() != 11) {
            return "fail";
        }

        if (!RegexCheckUtil.checkMobile(phone)) {
            return "fail";
        }

        iSmsService.sendSmsCode(phone);
        return "true";
    }

    /**
     * 手机验证码登录或者账号密码登录逻辑
     */
    public RoomResult mobileLogin(UserMobileLoginVO vo) {

        //普通用户的账号就是手机号
        String mobile = vo.getUsername();
        String code = vo.getCode();

        String validate = BeanValidator.validate(vo, UserMobileLoginVO.GroupInterface.class);

        //手机验证码快捷登录
        if (validate.equals("password")) {
            boolean b = iSmsService.verifyCode(mobile, code);
            if (!b) {
                return RoomResult.error("验证码错误");
            }
            return doLogin(vo);
        }

        //账号密码登录,有可能是管理员登录
        if (validate.equals("code")) {
            RoomResult success = RoomResult.success();
            String jwtToken = createJwtToken(mobile, vo.getPassword(), vo.isRememberMe());
            success.put(Constants.TOKEN, jwtToken);
            return success;
        }
        return RoomResult.error("密码或者验证码错误");
    }


    /**
     * 手机验证码快捷登录逻辑
     * 1.如果没有用户则新建用户
     * @param vo loginDTO
     */
    private RoomResult doLogin(UserMobileLoginVO vo) {
        String mobile = vo.getUsername();
        RoomResult result = RoomResult.success();
        return roomUserRepository.findByMobile(mobile).
            map(
                roomUser -> {
                    log.info("成功登录");
                    //手机号码快捷登录的化，账号密码相同
                    String jwtToken = createJwtToken(mobile, mobile, vo.isRememberMe());
                    result.put(Constants.TOKEN, jwtToken);
                    return result;
                }
            ).
            orElseGet(
                () -> {
                    RoomUser roomUser = new RoomUser();
                    roomUser.account(mobile).password(encoder.encode(mobile))
                        .avatar(DEFAULT_AVATAR)
                        .nickName(mobile)
                        .mobile(mobile)
                        .setActivated(true);
                    roomUserRepository.save(roomUser);
                    String jwtToken = createJwtToken(mobile, mobile, vo.isRememberMe());
                    result.put(Constants.TOKEN, jwtToken);
                    return result;
                }
            );
    }

    /**
     * 注册
     * @param dto 注册传参DTO
     */
    public RoomResult userRegister(UserRegisterDTO dto) {

        RoomResult result = RoomResult.success();

        //验证验证码
        String account = dto.getAccount();
        String code = dto.getCode();

        boolean verifyCode = iSmsService.verifyCode(account, code);
        if (!verifyCode) {
            return RoomResult.error("验证码错误");
        }

        //前台注册账号只能是电话号码
        if (!RegexCheckUtil.checkMobile(account)) {
            return RoomResult.error("请使用正确的电话号码");
        }

        RoomUser roomUser = roomUserRepository.findByAccount(dto.getAccount()).orElse(new RoomUser());
        //已经被注册了
        if (!Objects.isNull(roomUser.getAccount())) {
            return RoomResult.error("该用户已被注册了");
        }

        roomUser.account(account)
            .mobile(account)
            .nickName(account)
            .slogan(null)
            .password(encoder.encode(dto.getPassword()))
            .setActivated(true);

        roomUserRepository.save(roomUser);
        log.info("成功登录");

        String token = createJwtToken(dto.getAccount(),dto.getPassword(),dto.isRememberMe());
        result.put(Constants.TOKEN, token);
        return result;
    }

    //创建token返回到前端
    private String createJwtToken(String account, String password, boolean rememberMe) {
        //验证账号密码
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(account, password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        return tokenProvider.createToken(authenticationToken, rememberMe);
    }

    /**
     * 获取用户信息
     */
    public RoomResult getUserInfo() {
        String username = SecurityUtils.getCurrentUserLogin().orElse(null);
        RoomUser roomUser = roomUserRepository.findByAccount(username).orElse(new RoomUser());
        Set<Authority> authorities = roomUser.getAuthorities();
        //避免jpa的自动更新
        RoomUserDTO roomUserDTO = roomUserMapper.toDto(roomUser);
        Set<String> roles = new HashSet<>();
        for (Authority authority : authorities) {
            roles.add(authority.getName());
        }
        String permissions = roomUser.getPerms();
        Set<String> perms = Objects.isNull(permissions) ? null : new HashSet<>(Arrays.asList(roomUser.getPerms().split(",")));
        RoomResult roomResult = RoomResult.success();
        //密码不返回
        roomResult.put("user", roomUserDTO);
        roomResult.put("roles", roles);
        roomResult.put("permissions", perms);
        return roomResult;
    }

    /**
     * 获取所有的用户
     */
    public TableDataInfo getAllUsers(RoomUserDTO dto) {

        Pageable pageable = PageUtils.startPage();

        Page<RoomUser> all = roomUserRepository.findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (dto.getAccount() != null) {
                predicates.add(criteriaBuilder.equal(root.get("account"), dto.getAccount()));
            }

            if (dto.getMobile() != null) {
                predicates.add(criteriaBuilder.equal(root.get("mobile"), dto.getMobile()));
            }

            if (dto.getActivated() != null) {
                predicates.add(criteriaBuilder.equal(root.get("activated"), dto.getActivated()));
            }
            return query.where(predicates.toArray(new Predicate[0])).getRestriction();

        }, pageable);

        return getTableDataInfo(all);
    }

    private TableDataInfo getTableDataInfo(Page<RoomUser> all) {
        List<RoomUserDTO> roomUserDTOS = roomUserMapper.toDto(all.getContent());
        TableDataInfo dataInfo = new TableDataInfo();
        dataInfo.setCode(HttpStatus.SUCCESS);
        dataInfo.setMsg("查询成功");
        dataInfo.setRows(roomUserDTOS);
        dataInfo.setTotal(all.getTotalElements());
        return dataInfo;
    }

    /**
     * 用户状态修改
     */
    public RoomResult changeActivated(ChangeActivatedDTO dto) {
        Long id = dto.getId();
        RoomUser roomUser = roomUserRepository.findById(id).orElse(null);
        if (Objects.isNull(roomUser)) {
            return RoomResult.error("id错误");
        }
        if ("admin".equals(roomUser.getAccount())) {
            return RoomResult.error("不允许操作超级管理员用户");
        }
        roomUser.setActivated(dto.getActivated());
        return RoomResult.success("用户状态修改成功");
    }

    /**
     * 根据用户编号获取详细信息
     */
    public RoomResult addUserInfo() {

        RoomResult result = RoomResult.success();
        //获取除了系统管理员的其他角色集合
        Set<Authority> authorities = authorityRepository.findAll().
            stream().filter(authority -> !authority.getName().equals(AuthoritiesConstants.ADMIN)).collect(Collectors.toSet());

        Set<RoomUserPermission> perms = roomUserPermissionRepository.findAll().
            stream().filter(roomUserPermission -> !roomUserPermission.getPerm().equals(AuthoritiesConstants.PERM)).collect(Collectors.toSet());

        result.put("roles", authorities);
        result.put("perms", perms);
        return result;
    }


    /**
     * 管理员新增用户
     */
    public RoomResult addUser(RoomUserAddDTO dto) {

        String account = dto.getAccount();
        String mobile = dto.getMobile();
        Set<Authority> authorities = new HashSet<>();

        if (!CollectionUtils.isEmpty(dto.getRoleIds())) {
            for (String roleId : dto.getRoleIds()) {
                Authority authority = new Authority();
                authority.setName(roleId);
                authorities.add(authority);
            }
        }

        return roomUserRepository.findByAccountOrMobile(account, mobile).
            map(roomUser -> {
                //检测手机号的唯一性
                if (roomUser.getAccount().equals(account)) {
                   return RoomResult.error("新增用户'" + roomUser.getAccount() + "'失败，登录账号已存在");
                }
                //新增的用户是否已经注册
                return RoomResult.error("新增用户'" + roomUser.getAccount() + "'失败，手机号码已经已存在");
            }).orElseGet(
            ()->{
                RoomUser user = new RoomUser();
                user
                    .nickName(dto.getNickName())
                    .account(account)
                    .mobile(mobile)
                    .password(encoder.encode(dto.getPassword()))
                    .setPerms(dto.getPermIds())
                    .setAuthorities(authorities)
                    .avatar("https://gitee.com/guo-dashuaibright/photo/raw/master/admin_avatar.JPG")
                    .slogan(dto.getSlogan())
                    .setActivated(dto.getActivated());
                roomUserRepository.save(user);
                return RoomResult.success("新增用户成功");
            }
        );
    }


    public RoomResult brokerRegister(BrokerRegisterDTO dto) {

        if (!validCaptcha(dto.getCode(), dto.getUuid())) {
            return RoomResult.error("验证码错误");
        }

        String username = dto.getUsername();

        if (roomUserRepository.findByAccount(username).isPresent()) {
            return RoomResult.error("该账号已被注册");
        }

        RoomUserPermission roomUserPermission = roomUserPermissionRepository.findById(3L).orElse(new RoomUserPermission());
        Authority authority = authorityRepository.findById(BROKER).orElse(new Authority());

        RoomUser roomUser = new RoomUser()
            .account(username)
            .nickName(username)
            .password(encoder.encode(dto.getPassword()))
            .setActivated(true)
            .createdDate(Instant.now())
            .createdBy(username)
            .avatar("http://101.33.238.3:8888/group1/M00/00/00/CgAMD2I9uJ6AT3C-AAFI7wG7A10514.jpg")
            .setPerms(roomUserPermission.getPerm())
            .setProvince(dto.getProvince())
            .setCity(dto.getCity())
            .setDistrict(dto.getDistrict())
            .setAuthorities(Set.of(authority));

        roomUserRepository.save(roomUser);

        return RoomResult.success("success");
    }

    /**
     *
     */
    private boolean validCaptcha(String code, String uuid) {
        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if (captcha == null)
        {
            return false;
        }
        return code.equalsIgnoreCase(captcha);
    }


    public RoomResult getBroker(GetBrokerDTO dto) {
        List<RoomUser> all = roomUserRepository.findAll((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (dto.getProvince() != null) {
                predicates.add(criteriaBuilder.equal(root.get("province"), dto.getProvince()));
            }
            if (dto.getCity() != null) {
                predicates.add(criteriaBuilder.equal(root.get("city"), dto.getCity()));
            }
            if (dto.getDistict() != null) {
                predicates.add(criteriaBuilder.equal(root.get("district"), dto.getDistict() + "%"));
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        List<RoomUserDTO> roomUserDTOS = roomUserMapper.toDto(all);
        return RoomResult.success(roomUserDTOS);
    }
}
